Дата 23.02.01 11:34 От кого Vlad Mogilevsky <vladosha@inbox.ru> Кому <kaev@yandex.ru>
Задача программы была получив имя каталога, поискать в нем все каталоги. Получив список этих каталогов войти внутрь каждого и выбрать оттуда все файлы с определенным расширением. Тупой перебор рекурсивной функцией в один поток дает время выполнения около 2 часов при 200 тыс каталогов. Исходя из этого было решено использовать многопоточный поиск внутри нижнего уровня. Программа контролирует максимальное количество одновременно запущенных потоков при помощи iCurrentRun и не дает запустить более 10. Задержка между попытками запуска задана в расчете на 0.1 секунду (равна 10*100=1 секунда). Для синхронизации нескольких потоков и гарантии, что новый поток получит входной параметр правильно используется m_Processed. Переменная m_AllExited сигнализирует о завершении последнего потока. Через strScanDir передается в поток имя каталога, в котором нужно искать. В поток через pParam передается ссылка на интерфейс, вызвавший поток. Через эту ссылку можно передать сообщение интерфейсу от потока.
Внутри потока при некой обработке я использовал критическую секцию, которая общалась с некоторой глобальной переменной. Чтобы от момента считывания переменной до ее обновления потоком, другой поток ее не изменил и стоит критическая секция.
Рекомендации: ограничение потоков необходимо для того, чтобы программа вообще смогла завершиться. При бесконечном количестве - она виснет напрочь. При маленьком (например 1 - выполняется долго). Оптимальное количество 5-10 потоков. Ставить 400-500 штук тоже не следует - больше чем может вытянуть диск, вы из него выжать не сможете. Мой пример после этого стал работать не 2 часа, а 30-40 минут. Загрузка процессора небольшая.
#define EXTENSION txt
static CEvent m_Processed; //event semaphore
static CEvent m_AllExited; //event semaphore
static CString strScanDir; //global parameter: directory
static long iCurrentRun; //current threads running
static CString strExt; //extension of files to look for
void CDialogApp::Recurse(LPCTSTR pstr) //name of root directory to search in
{
CString str, tmpStr;
int iThreads, iThreadRetry;
CFileFind finder;
strExt=EXTENSION;
iThreads=10; //threads to start
iThreadRetry=10; //retry to start thread in 0.1 seconds
// build a string with wildcards
CString strWildcard(pstr);
strWildcard += _T("\\*"); //just only directories
// start working for files
BOOL bWorking = finder.FindFile(strWildcard);
while (bWorking)
{
bWorking = finder.FindNextFile();
if (finder.IsDots()) // skip . and .. files; otherwise, we'd recur infinitely!
continue;
// if it's a directory, search into it
str = finder.GetFilePath();
if (finder.IsDirectory())
{
//start thread for finding
m_Processed.ResetEvent();
strScanDir=str; //parameter for search - directory name
//don't make more threads?
if(iCurrentRun>=iThreads)
while(iCurrentRun>=iThreads)
Sleep(iThreadRetry*100);
CWinThread* myWinThread = AfxBeginThread(*ProcessScan, GetSafeHwnd());
WaitForSingleObject(m_Processed,INFINITE);
}
}
finder.Close(); //stop looking for files
}
UINT ProcessScan(LPVOID pParam)
{
CString strDir;
strDir=strScanDir;
m_AllExited.ResetEvent(); //just entered thread
m_Processed.SetEvent(); //process main thread
//process directory
InterlockedIncrement((long*)&iCurrentRun); //current thread running counter increment
//PostMessage((HWND) pParam, WM_REFRESH_STATUS, 0,0);
//You can post any message to the interface
CCriticalSection m_Lock; //critical section object
CFileFind findFile;
if(strExt.Left(1)==".")
strExt=strExt.Mid(2); //cut "." at left position, if exists
strDir += _T("\\*."); //only "*.<extension>"
strDir +=strExt;
// start working for files
BOOL bWorking = findFile.FindFile(strDir);
while (bWorking)
{
bWorking = findFile.FindNextFile();
if (findFile.IsDots()) // skip . and .. files; otherwise, we'd recur infinitely!
continue;
if(!findFile.IsDirectory()) //this is a file
{
str=findFile.GetFileName();
//this's a file!
//.... process it!
m_Lock.Lock(); //block code until we calculate something
// do something, while blocking code
//You don't have to use CriticalSection!
//I used it only because of unsafe calculations
m_Lock.Unlock(); //unblock code
}
}
InterlockedDecrement((long*)&iCurrentRun);
if (iCurrentRun==0)
m_AllExited.SetEvent(); //exited all threads?
return 0;
}
Шаг прислал Vlad Mogilevsky.